﻿using Mono.Cecil.Rocks;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace libCodeGen
{

    //SDK 工具
    public class SDKTool
    {
        List<ConvertDLL> allDlls = new List<ConvertDLL>();

        public IReadOnlyList<ConvertDLL> AllDlls
        {
            get
            {
                return allDlls.AsReadOnly();
            }
        }
        //从dotnet dll 文件中 加载他的所有类型，准备导出

        public ConvertDLL GetConvertDll(string fullname)
        {
            foreach (var dll in allDlls)
            {
                if (dll.monoAssembly.FullName == fullname)
                {
                    return dll;
                }
            }
            return null;
        }
        ConvertType skiptype = null;
        public ConvertType GetConvertType(string fullname)
        {
            if (skiptype == null)
                skiptype = new ConvertType(this, null);
            if (
                fullname == "System.Int16" ||
                fullname == "System.Int32" ||
                fullname == "System.Int64" ||
                fullname == "System.IntPtr" ||
                fullname == "System.UInt16" ||
                fullname == "System.UInt32" ||
                fullname == "System.UInt64" ||
                fullname == "System.Byte" ||
                fullname == "System.SByte" ||
                fullname == "System.Char" ||

                fullname == "System.Single" ||

                fullname == "System.Double" ||

                fullname == "System.String" ||
                fullname == "System.Boolean" ||
                fullname == "System.Void")
            {
                return skiptype;
            }
            foreach (var dll in allDlls)
            {
                foreach (var t in dll.RefTypes)
                {
                    if (t.monoType.FullName == fullname)
                    {
                        return t;
                    }
                }
            }
            return null;
        }
        public void LoadDLL(string path, string filename, bool LoadDepend)
        {
            var fullfile = System.IO.Path.Combine(path, filename);

            Mono.Cecil.AssemblyDefinition assembly = Mono.Cecil.AssemblyDefinition.ReadAssembly(fullfile);
            if (GetConvertDll(assembly.FullName) != null)
                throw new Exception("already loaded assembly :" + assembly.FullName);

            allDlls.Add(new ConvertDLL(this, assembly));
            if (LoadDepend)
            {
                foreach (var m in assembly.Modules)
                {
                    if (m.HasAssemblyReferences)
                    {
                        foreach (var refass in m.AssemblyReferences)
                        {
                            if (GetConvertDll(refass.FullName) != null)
                            {
                                continue;
                            }
                            else
                            {
                                try //加载依赖出错就算了
                                {
                                    LoadDLL(path, refass.Name, true);
                                }
                                catch
                                {

                                }
                            }
                        }
                    }
                }
            }
        }
    }



    public interface IExportOption
    {

        bool IsExport
        {
            get; set;
        }
        bool TestExport(out string info);

    }

    public class ConvertDllRef
    {
        public string fullname;
        public ConvertDLL dll;
    }
    public class ConvertDLL : IExportOption
    {
        SDKTool tool;
        //引用的dll
        List<ConvertDllRef> refDlls = new List<ConvertDllRef>();
        List<ConvertType> refTypes = new List<ConvertType>();

        public ConvertDLL(SDKTool tool, Mono.Cecil.AssemblyDefinition _assembly)
        {
            this.tool = tool;
            this.monoAssembly = _assembly;
            IsExport = true;

            foreach (var m in monoAssembly.Modules)
            {
                if (m.HasAssemblyReferences)
                {
                    foreach (var refass in m.AssemblyReferences)
                    {
                        ConvertDllRef _ref = new ConvertDllRef();
                        _ref.fullname = refass.FullName;
                        _ref.dll = tool.GetConvertDll(refass.FullName);
                        this.refDlls.Add(_ref);
                    }
                }
                if (m.HasTypes)
                {
                    foreach (var t in m.Types)
                    {
                        if (t.Name[0] == '<') continue;
                        if (!t.IsPublic) continue;
                        ConvertType ct = new ConvertType(tool, t);
                        this.refTypes.Add(ct);
                    }

                }
            }
        }
        public bool IsExport
        {
            get;
            set;
        }
        public bool TestExport(out string info)
        {
            //检查这玩意能不能导出
            info = null;
            foreach (var t in this.refTypes)
            {
                if (t.IsExport)
                {
                    if (t.TestExport(out info) == false)
                        return false;
                }
            }
            return true;
        }

        public Mono.Cecil.AssemblyDefinition monoAssembly
        {
            get;
            private set;
        }
        public IReadOnlyList<ConvertDllRef> RefDLLs
        {
            get
            {
                foreach (var _ref in refDlls)
                {
                    if (_ref.dll == null)
                        _ref.dll = tool.GetConvertDll(_ref.fullname);
                }
                return refDlls.AsReadOnly();
            }
        }
        public IReadOnlyList<ConvertType> RefTypes
        {
            get
            {
                return refTypes.AsReadOnly();
            }
        }
    }
    public class ConvertType : IExportOption
    {
        SDKTool tool;
        public Mono.Cecil.TypeDefinition monoType
        {
            get;
            private set;
        }//类型定义
        List<ConvertCtor> ctors = new List<ConvertCtor>();
        List<ConvertMethod> methods = new List<ConvertMethod>();
        List<ConvertField> fields = new List<ConvertField>();
        public IReadOnlyList<ConvertCtor> Ctors
        {
            get
            {
                return ctors;
            }
        }
        public IReadOnlyList<ConvertMethod> Methods
        {
            get
            {
                return methods;
            }
        }
        public IReadOnlyList<ConvertField> Fields
        {
            get
            {
                return fields;
            }
        }
        public ConvertType(SDKTool tool, Mono.Cecil.TypeDefinition monoType)
        {
            this.tool = tool;
            this.monoType = monoType;
            if (monoType != null)
            {
                IsExport = true;
                var ctors = monoType.GetConstructors();
                foreach (var c in ctors)
                {
                    if (!c.IsPublic) continue;
                    this.ctors.Add(new ConvertCtor(tool, c));
                }
                foreach (var m in monoType.Methods)
                {
                    if (!m.IsPublic) continue;

                    if (m.Name == ".ctor" || m.Name == ".cctor")
                        continue;
                    this.methods.Add(new ConvertMethod(tool, m));
                }
                foreach (var f in monoType.Fields)
                {
                    if (!f.IsPublic) continue;
                    this.fields.Add(new ConvertField(tool, f));
                }
            }
        }
        public bool IsExport
        {
            get;
            set;
        }
        public bool TestExport(out string info)
        {
            //检查这玩意能不能导出
            info = null;
            foreach (var c in this.ctors)
            {
                if (c.IsExport)
                {
                    if (c.TestExport(out info) == false)
                    {
                        return false;
                    }
                }
            }
            foreach (var m in this.methods)
            {
                if (m.IsExport)
                {
                    if (m.TestExport(out info) == false)
                    {
                        return false;
                    }
                }
            }
            foreach (var f in this.fields)
            {
                if (f.IsExport)
                {
                    if (f.TestExport(out info) == false)
                    {
                        return false;
                    }
                }
            }
            return true;
        }
    }

    public class ConvertMethod : IExportOption
    {
        SDKTool tool;
        public Mono.Cecil.MethodDefinition monoMethod
        {
            get;
            private set;
        }
        public ConvertMethod(SDKTool tool, Mono.Cecil.MethodDefinition monoMethod)
        {
            this.tool = tool;
            this.monoMethod = monoMethod;
            this.IsExport = true;
        }
        public bool IsExport
        {
            get;
            set;
        }
        public bool TestExport(out string info)
        {
            //检查这玩意能不能导出 ,检查参数

            info = null;
            foreach (var p in monoMethod.Parameters)
            {
                var type = tool.GetConvertType(p.ParameterType.FullName);
                if (type == null)
                {
                    info = "can't find type:" + p.ParameterType.FullName + " for:" + monoMethod.FullName;
                    return false;
                }
            }
            var rtype = tool.GetConvertType(monoMethod.ReturnType.FullName);
            if (rtype == null)
            {
                info = "can't find type:" + monoMethod.ReturnType.FullName + " for:" + monoMethod.FullName;
                return false;

            }
            return true; ;
        }
    }
    public class ConvertField : IExportOption
    {
        SDKTool tool;
        public Mono.Cecil.FieldDefinition monoField
        {
            get;
            private set;
        }

        public ConvertField(SDKTool tool, Mono.Cecil.FieldDefinition monoField)
        {
            this.tool = tool;
            this.monoField = monoField;
            this.IsExport = true;
        }
        public bool IsExport
        {
            get;
            set;
        }
        public bool TestExport(out string info)
        {
            var type = tool.GetConvertType(monoField.FieldType.FullName);
            if (type == null)
            {
                info = "can't find type: " + monoField.FieldType + " for:" + monoField.FullName;
                return false;
            }
            else
            {
                info = null;
                return true;
            }
        }
    }
    public class ConvertCtor : IExportOption
    {
        SDKTool tool;
        public Mono.Cecil.MethodDefinition monoCtor
        {
            get;
            private set;
        }
        public ConvertCtor(SDKTool tool, Mono.Cecil.MethodDefinition monoCtor)
        {
            this.tool = tool;
            this.monoCtor = monoCtor;
            this.IsExport = true;
        }
        public bool IsExport
        {
            get;
            set;
        }
        public bool TestExport(out string info)
        {
            info = null;
            foreach (var p in monoCtor.Parameters)
            {
                var type = tool.GetConvertType(p.ParameterType.FullName);
                if (type == null)
                {
                    info = "can't find type:" + p.ParameterType.FullName + " for:" + monoCtor.FullName;
                    return false;
                }
            }
            return true; ;
        }
    }

}
